#include <thread>
#include <chrono>
#include <cassert>
#include <iostream>

#include <Windows.h>

#include "Application.hpp"
#include "Input.hpp"
#include "Event.hpp"
#include "Renderer.hpp"

#include "Debug.hpp"

CApplication::CApplication(int framerate) : eventHandler(nullptr), shutdown(false), framerate(framerate), frame(0)
{
	assert(framerate && framerate <= 1000 && (1000 % framerate) == 0);
	ticksperframe = 1000 / framerate;
}

CApplication::~CApplication()
{
	delete inputManager;
	delete eventHandler;
}

void CApplication::Run()
{
	using namespace std::chrono;

	eventHandler = new EventHandler;
	eventHandler->AddListener(EventType::Shutdown, std::bind(&CApplication::OnShutdown, this, std::placeholders::_1));
	//eventHandler->AddListener(EventType::MouseClick, std::bind(&CApplication::OnMouseClick, this, std::placeholders::_1));

	inputManager = new CInput(*eventHandler);
	CRenderer* renderer = new CRenderer(4, 1, *eventHandler);

	time_point<high_resolution_clock> now;
	time_point<high_resolution_clock> nextFrame;
	time_point<high_resolution_clock> lastFrame;

	lastFrame = now = high_resolution_clock::now();
	nextFrame = now + milliseconds(ticksperframe);

	KeyInput* keyboard; //= nullptr;// = new KeyInput;
	MouseInput* mouse; // = nullptr;// = new MouseInput;

	while(true)
	{
		if(shutdown)
			break;

		now = high_resolution_clock::now();
		auto diff = duration_cast<milliseconds>(now - lastFrame);

		if(ticksperframe > diff.count())
			std::this_thread::sleep_for(milliseconds(1));
		else
		{
			//UpdateInput
			keyboard = inputManager->GetKeyInput();
			mouse = inputManager->GetMouseInput();
			inputManager->SwapInputBuffers();

			//UpdatePhysics
			//UpdateGame
			if(keyboard->keyStates['W']->isDown)
			{
				DebugBreak();
			}
			else if(keyboard->keyStates['A']->isDown)
			{
				DebugBreak();
			}
			else if(keyboard->keyStates['S']->isDown)
			{
				DebugBreak();
			}
			else if(keyboard->keyStates['D']->isDown)
			{
				DebugBreak();
			}

			//PlaySound
			frame++;
	
			nextFrame = now + duration<long long, std::milli>(ticksperframe) - diff - milliseconds(ticksperframe);
			lastFrame = now;
		}
	}

	delete renderer;
}

// Event handlers
bool CApplication::OnShutdown(Event*)
{
	shutdown = true;
	return true;
}

bool CApplication::OnMouseInput(Event* evt)
{
	//auto data = static_cast<RawInput*>(evt->data);

	return true;
}